#include "Module/ConnectionManagementWidget.qth"
#include "Module/ConnectionManagementWidget.moh"
#include "Module/ModuleRegistry.hpp"
#include "AbsUI/DummyConnConfigWidget.qth"
#include "AbsUI/ConnCfgWidgetFactory.hpp"
#include "Storage/ConnectionConfig.hpp"
#include "Storage/ConnectionConfigCollection.hpp"
#include "Storage/ConnectionConfigFactory.hpp"
#include <QtGui/QHBoxLayout>
#include <QtGui/QVBoxLayout>
#include <QtGui/QGridLayout>
#include <QtGui/QLabel>
#include <QtGui/QListWidget>
#include <QtGui/QLineEdit>
#include <QtGui/QStackedLayout>
#include <QtGui/QPushButton>
#include <QtGui/QComboBox>
#include <QtGui/QItemSelectionModel>
#include <QtGui/QMessageBox>
#include <QtCore/QCoreApplication>
#include <cassert>
#include <algorithm>

Q_DECLARE_METATYPE( Storage::ConnectionConfig*)

namespace Module
{

ConnectionManagementWidget::ConnectionManagementWidget( QWidget* pParent /*= NULL*/ )
	: QWidget( pParent)
	, pStorageConfigWidget_( NULL)
{
	QGridLayout* pLayout = new QGridLayout();
	this->setLayout( pLayout);

	// Existing connections
	pExistingConnList_ = new QListWidget();
	pExistingConnList_->setSelectionMode( QAbstractItemView::SingleSelection);
	QLabel* pConnListLabel = new QLabel( pExistingConnList_);
	pConnListLabel->setText( tr( "Existing connections", "Connection list label"));
	pLayout->addWidget( pConnListLabel, 0, 0);
	pLayout->addWidget( pExistingConnList_, 1, 0);

	// Adding support
	QHBoxLayout* pNewConnLayout = new QHBoxLayout();
	pLayout->addLayout( pNewConnLayout, 2, 0);
	// Combobox with supported connection type names.
	pSupportedConnTypeNames_ = new QComboBox();
	std::list< std::string> supportedTypeNames;

	// Populate the list of connection type names.
	std::list< Storage::NameString> connCfgTypeNames;
	Module::ModuleRegistry::instance().connConfigFactory().getRegisteredConnCfgTypes( connCfgTypeNames);
	std::list< Storage::NameString>::const_iterator curNameIter = connCfgTypeNames.begin(), 
		nameEndIter = connCfgTypeNames.end();
	while( curNameIter != nameEndIter)
	{
		pSupportedConnTypeNames_->addItem( curNameIter->c_str(), QString::fromStdString(*curNameIter));
		++curNameIter;
	}
	pNewConnLayout->addWidget( pSupportedConnTypeNames_);
	
	QPushButton* pAddNewConnectionButton = new QPushButton( tr( "Add", "Add new connection push button label"));
	pNewConnLayout->addWidget( pAddNewConnectionButton);
	pAddNewConnectionButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed);
	connect( pAddNewConnectionButton, SIGNAL( clicked()), this, SLOT( addNewConnection()));
	connect( pExistingConnList_, SIGNAL( itemSelectionChanged()), 
			 this, SLOT( currentConnCfgChanged()));

	// Current connection configuration
	pConnCfgLayout_ = new QVBoxLayout();
	QLabel* pConnDescrLabel = new QLabel();
	pConnDescrLabel->setText( tr( "Selected connection", "Selected connection label"));
	pStorageConfigWidget_ = new AbsUI::DummyConnConfigWidget();
	pLayout->addWidget( pConnDescrLabel, 0, 1);
	pConnCfgLayout_->addWidget( pStorageConfigWidget_);
	pConnCfgLayout_->addStretch();
	pLayout->addLayout( pConnCfgLayout_, 1, 1);

	// Selection operations
	QHBoxLayout* pSelOpsLayout = new QHBoxLayout();
	pLayout->addLayout( pSelOpsLayout, 2, 1);
	pSelOpsLayout->addStretch();
	pUpdateConnectionButton_ = new QPushButton( tr( "Update", "Update connection push button caption"));
	pSelOpsLayout->addWidget( pUpdateConnectionButton_);
	pDeleteConnectionButton_ = new QPushButton( tr( "Delete", "Delete connection push button caption"));
	pSelOpsLayout->addWidget( pDeleteConnectionButton_);
	connect( pDeleteConnectionButton_, SIGNAL( clicked( bool)), this, SLOT( deleteCurrentConfig()));

	this->populateConnList();
}

// ---------------------------------------------------------------------------------------------------------------------

void ConnectionManagementWidget::populateConnList()
{
	using namespace Storage;
	ConnectionConfigCollection& connections = ModuleRegistry::instance().connCfgCollection();
	ConnectionConfigCollection::ConstIterator curI = connections.begin(),
		endI = connections.end();
	while( curI != endI)
	{
		QListWidgetItem* pNewItem = this->createConnectionListItem( *curI);
		pExistingConnList_->addItem( pNewItem);
		++curI;
	}
}

// ---------------------------------------------------------------------------------------------------------------------

void ConnectionManagementWidget::addNewConnection()
{
	// Identify selected connection configuration type.
	assert( pSupportedConnTypeNames_->currentIndex() >= 0);
	QVariant selectionData = pSupportedConnTypeNames_->itemData( pSupportedConnTypeNames_->currentIndex());
	std::string connTypeName = (qvariant_cast< QString>( selectionData)).toStdString();
	
	// Create the connection configuration widget corresponding to the specified type.
	AbsUI::ConnectionConfigWidget* pNewConfigWidget 
		= Module::ModuleRegistry::instance().connCfgWidgetFactory().createConnCfgWidget( connTypeName);
	assert( NULL != pNewConfigWidget);

	// Replace the widget
	this->replaceConfigWidget( pNewConfigWidget);

	// Of course, no configuration from the list should be selected for the moment.
	this->clearSelectedConnCfg();
}

// ---------------------------------------------------------------------------------------------------------------------

void ConnectionManagementWidget::replaceConfigWidget( AbsUI::ConnectionConfigWidget* pConfigWidget )
{
	assert( NULL != pConfigWidget);
	if( pStorageConfigWidget_->isDirty())
	{
		// In case of dirty flat set for the old configuration widget, ask for confirmation and update the corresponding 
		// configuration in case of affirmative answer.
		QMessageBox::StandardButton result = QMessageBox::question( 
			this, 
			tr( "Save Changes to Current Connection Configuration"), 
			tr( "Do you want to save the changes you performed to the current configuration"), 
			QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
		if( result == QMessageBox::Yes)
		{
			this->updateEditedConfig();
		}
	}
	delete pStorageConfigWidget_;

	// Change the currently displayed configuration widget.
	pStorageConfigWidget_ = pConfigWidget;
	pConfigWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum);
	pConnCfgLayout_->insertWidget( 0, pStorageConfigWidget_);
	connect( pConfigWidget, SIGNAL( nameDefined( bool)), pUpdateConnectionButton_, SLOT( setDisabled( bool)));
	connect( pUpdateConnectionButton_, SIGNAL( clicked()), this, SLOT( updateEditedConfig()));
	pUpdateConnectionButton_->setDisabled( pStorageConfigWidget_->connectionName().isEmpty());
}

// ---------------------------------------------------------------------------------------------------------------------

void ConnectionManagementWidget::updateEditedConfig()
{
	// Save the data from the widget to the corresponding configuration.
	pStorageConfigWidget_->updateEditedConfig();
	
	//  Now, update this widget layout
	Storage::ConnectionConfig* pEditedConnection = pStorageConfigWidget_->editedConnCfg();
	QListWidgetItem* pSelectedItem = this->getSelectedConnCfgItem();
	if( NULL == pSelectedItem)
	{
		// The updated configuration was a new one (no corresponding in the list of available configurations), so it has
		// to be added to the list of available configurations.

		QListWidgetItem* pNewItem = this->createConnectionListItem( pEditedConnection);
		pExistingConnList_->addItem( pNewItem);
		pNewItem->setSelected( true);
		
		using namespace Storage;
		ConnectionConfigCollection& connections = ModuleRegistry::instance().connCfgCollection();
		assert( connections.end() == std::find( connections.begin(), connections.end(), pEditedConnection));
		connections.addConnectionConfig( pEditedConnection);
	}
	else
	{
		assert( pSelectedItem->data( Qt::UserRole).value< Storage::ConnectionConfig*>() == pEditedConnection);
		pSelectedItem->setText( QString::fromStdString( pEditedConnection->name()));
	}	
}

// ---------------------------------------------------------------------------------------------------------------------

QListWidgetItem* ConnectionManagementWidget::createConnectionListItem( Storage::ConnectionConfig* pConnCfg )
{
	QListWidgetItem* pNewItem = new QListWidgetItem();
	pNewItem->setText( QString::fromStdString( pConnCfg->name()));
	QVariant data;
	data.setValue< Storage::ConnectionConfig*>( pConnCfg);
	pNewItem->setData( Qt::UserRole, data);
	return pNewItem;
}

// ---------------------------------------------------------------------------------------------------------------------

void ConnectionManagementWidget::currentConnCfgChanged()
{
	Storage::ConnectionConfig* pCurrentConfig = this->getConnectionConfig( this->getSelectedConnCfgItem());
	if( pStorageConfigWidget_->editedConnCfg() == pCurrentConfig)
	{
		// False alarm: the edited configuration didn't changed. So we do nothing.
		return;
	}

	if( NULL == pCurrentConfig)
	{
		// If no configuration is selected, just display the default one.
		this->replaceConfigWidget( new AbsUI::DummyConnConfigWidget);
	}
	else
	{
		AbsUI::ConnectionConfigWidget* pNewCfgWidget = dynamic_cast< AbsUI::ConnectionConfigWidget*>( 
			ModuleRegistry::instance().connCfgWidgetFactory().createConnCfgWidget( pCurrentConfig->typeName()));
		pNewCfgWidget->setEditedConfig( pCurrentConfig);
		this->replaceConfigWidget( pNewCfgWidget);
	}
	pDeleteConnectionButton_->setEnabled( NULL != pCurrentConfig);
	pUpdateConnectionButton_->setEnabled( NULL != pCurrentConfig);
}

// ---------------------------------------------------------------------------------------------------------------------

Storage::ConnectionConfig* ConnectionManagementWidget::getConnectionConfig( QListWidgetItem* pListItem )
{
	if( NULL == pListItem)
	{
		return NULL;
	}

	QVariant data = pListItem->data( Qt::UserRole);
	if( data.isNull() || !data.isValid())
	{
		return NULL;
	}
	assert( data.canConvert< Storage::ConnectionConfig*>());
	return data.value< Storage::ConnectionConfig*>();

	return NULL;
}

// ---------------------------------------------------------------------------------------------------------------------

QListWidgetItem* ConnectionManagementWidget::getSelectedConnCfgItem()
{
	QList< QListWidgetItem*> selectedItems = pExistingConnList_->selectedItems();
	return selectedItems.isEmpty() ? NULL : *selectedItems.begin();
}

// ---------------------------------------------------------------------------------------------------------------------

void ConnectionManagementWidget::clearSelectedConnCfg()
{
	QListWidgetItem* pSelected = this->getSelectedConnCfgItem();
	if( NULL != pSelected)
	{
		pSelected->setSelected( false);
	}
}

// ---------------------------------------------------------------------------------------------------------------------

void ConnectionManagementWidget::deleteCurrentConfig()
{
	QListWidgetItem* selectedItem = this->getSelectedConnCfgItem();
	assert( NULL != selectedItem);
	Storage::ConnectionConfig* pEditedCfg = pStorageConfigWidget_->editedConnCfg();
	assert( NULL != pEditedCfg);

	ModuleRegistry::instance().connCfgCollection().removeConnectionConfig( pEditedCfg);
	delete pEditedCfg;

	// This should deselect from existing connection configuration list.
	delete selectedItem;
}

} // namespace Module
